﻿package  
{
	import flash.utils.ByteArray;
	/**
	 * ...
	 * @author Julian Garamendy - Miniclip LTD
	 */
	public class ElementaryCellularAutomaton
	{
		private var _length:uint;
		private var _rule:uint;
		private var _state:ByteArray = new ByteArray();
		
		public function ElementaryCellularAutomaton( length:uint = 8, rule:uint = 110, initial_state:ByteArray = null ) 
		{
			this.length = length;
			this.rule = rule;
			if (initial_state==null) initial_state = getEmptyState();
			this.state = initial_state;
		}
		
		public function update():ByteArray 
		{
			var new_state:ByteArray = new ByteArray();
			var n:uint = _state.length;
			for (var i:int = 0; i < n; i++) 
			{
				var right:Boolean;
				var left:Boolean;
				var centre:Boolean;
				
				if (i > 0 && i < n - 1)
				{
					_state.position = i - 1;
					right = _state.readBoolean();
					centre = _state.readBoolean();
					left = _state.readBoolean();
				}
				if (i == 0)
				{
					_state.position = _state.length - 1;
					right = _state.readBoolean();
					_state.position = i;
					centre = _state.readBoolean();
					left = _state.readBoolean();
				}
				if (i == n - 1)
				{
					_state.position = i - 1;
					right = _state.readBoolean();
					centre = _state.readBoolean();
					_state.position = 0;
					left = _state.readBoolean();
				}
				var pattern:uint = int(left) * 4 + int(centre) * 2 + int(right);
				var new_bit:Boolean = getNewState( pattern );
				new_state.writeBoolean( new_bit );
			}
			_state = new_state;
			return _state;
		}
		
		private function getNewState( pattern:uint ):Boolean 
		{
			if (pattern > 7) throw( new Error("pattern must be between 0 and 7. That is, betwen 000 and 111 binary") );
			
			var ba_rule:ByteArray = getRuleBits();
			ba_rule.position = pattern;	
			return ba_rule.readBoolean();
		}		
		
		private function getRuleBits():ByteArray
		{
			var r:ByteArray = new ByteArray();
			r.length = 8;
			var num:uint = _rule;
			
			while (num > 0)
			{
				r.writeBoolean( num % 2 != 0 );
				num = Math.floor(num / 2);
			}
			r.position = 0;
			return r;
		}
		
		private function getEmptyState():ByteArray 
		{
			var st:ByteArray = new ByteArray();
			for (var i:int = 0; i < _length; i++) st.writeBoolean( false );
			st.position = 0;
			return st;
		}
	
		
		///////////////
		
		public function get rule():uint { return _rule; }
		
		public function set rule(value:uint):void 
		{
			if (value > 255) value = 255;
			_rule = value;
		}		
		
		public function get length():uint { return _length; }
		
		public function set length(value:uint):void 
		{
			if (value > 0) _length = value;
		}
		
		public function get state():ByteArray { return _state; }
		
		public function set state(value:ByteArray):void 
		{
			_state = value;
		}
		
	}

}